/**
 * 
 */
package com.netscenery.yang.model;

import java.math.BigInteger;
import java.util.List;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author llly
 *
 */
public class YangInteger extends Type {
	
	private Range range;
	
	public YangInteger(){
		super();
	}
	/**
	 * constructor
	 * @param env
	 * @param name
	 */
	public YangInteger(Env env, BuiltinType base,String name){
		super(env,base,name);
	}
	/**
	 * constructor 
	 * @param env
	 */
	public YangInteger(Env env,BuiltinType base){
		super(env,base);
	}
	
	
	public Range getRange(){
		return range;
	}

	/**
	 * @return the range
	 */
	public Range getActualRange() {
		if (null == range){
			if (null != this.derived){
				return ((YangInteger)(this.derived.getType())).getActualRange();
			}
			else {
				if (name.equals(BuiltinType.UINT8.getFieldName())){
					return new Range(RangeEx.UINT8_LOW_BOUND,RangeEx.UINT8_HIGH_BOUND);
				}
				else if (name.equals(BuiltinType.UINT16.getFieldName())){
					return new Range(RangeEx.UINT16_LOW_BOUND,RangeEx.UINT16_HIGH_BOUND);
				}
				else if (name.equals(BuiltinType.UINT32.getFieldName())){
					return new Range(RangeEx.UINT32_LOW_BOUND,RangeEx.UINT32_HIGH_BOUND);
				}
				else if (name.equals(BuiltinType.UINT64.getFieldName())){
					return new Range(RangeEx.UINT64_LOW_BOUND,RangeEx.UINT64_HIGH_BOUND);
				}
				else if (name.equals(BuiltinType.INT8.getFieldName())){
					return new Range(RangeEx.INT8_LOW_BOUND,RangeEx.INT8_HIGH_BOUND);
				}
				else if (name.equals(BuiltinType.INT16.getFieldName())){
					return new Range(RangeEx.INT16_LOW_BOUND,RangeEx.INT16_HIGH_BOUND);
				}
				else if (name.equals(BuiltinType.INT32.getFieldName())){
					return new Range(RangeEx.INT32_LOW_BOUND,RangeEx.INT32_HIGH_BOUND);
				}
				else if (name.equals(BuiltinType.INT64.getFieldName())){
					return new Range(RangeEx.INT64_LOW_BOUND,RangeEx.INT64_HIGH_BOUND);
				}
				else {
					return null;
				}
			}
			
		}
		return this.range;
	}
	
	public boolean isValidRange(Range range){
		if (null == range){
			return false;
		}
		if (false == this.isDerivedType){
			BigInteger lowBound;
			BigInteger highBound;
			if (name.equals(BuiltinType.UINT8.getFieldName())){
				lowBound = RangeEx.UINT8_LOW_BOUND;
				highBound = RangeEx.UINT8_HIGH_BOUND;
			}
			else if (name.equals(BuiltinType.UINT16.getFieldName())){
				lowBound = RangeEx.UINT16_LOW_BOUND;
				highBound = RangeEx.UINT16_HIGH_BOUND;
			}
			else if (name.equals(BuiltinType.UINT32.getFieldName())){
				lowBound = RangeEx.UINT32_LOW_BOUND;
				highBound = RangeEx.UINT32_HIGH_BOUND;
			}
			else if (name.equals(BuiltinType.UINT64.getFieldName())){
				lowBound = RangeEx.UINT64_LOW_BOUND;
				highBound = RangeEx.UINT64_HIGH_BOUND;
			}
			else if (name.equals(BuiltinType.INT8.getFieldName())){
				lowBound = RangeEx.INT8_LOW_BOUND;
				highBound = RangeEx.INT8_HIGH_BOUND;
			}
			else if (name.equals(BuiltinType.INT16.getFieldName())){
				lowBound = RangeEx.INT16_LOW_BOUND;
				highBound = RangeEx.INT16_HIGH_BOUND;
			}
			else if (name.equals(BuiltinType.INT32.getFieldName())){
				lowBound = RangeEx.INT32_LOW_BOUND;
				highBound = RangeEx.INT32_HIGH_BOUND;
			}
			else if (name.equals(BuiltinType.INT64.getFieldName())){
				lowBound = RangeEx.INT64_LOW_BOUND;
				highBound = RangeEx.INT64_HIGH_BOUND;
			}
			else {
				return false;
			}
			if (range.getHighBound().compareTo(highBound) > 0){
				return false;
			}
			
			if (range.getLowBound().compareTo(lowBound) < 0){
				return false;
			}
			
		}
		else {
			YangInteger integer = (YangInteger)(this.getDerived().getType());
			Range derivedRange = null;
			while(null == derivedRange){
				derivedRange = integer.getActualRange();
				if (null != derivedRange){
					break;
				}
				
				integer = ((YangInteger)(integer.getDerived().getType()));
			}

			if (!derivedRange.isInRange(range)){
				return false;
			}
		}
		return true;
	}

	/**
	 * @param range the length to set
	 */
	public boolean setRange(Range range) {
		if (!isValidRange(range)){
			return false;
		}
		
		this.range = range;
		return true;
	}
	
	public Range createRange(){
		Range range = null;
		if (false == this.isDerivedType){
			range = new Range(new RangeEx(this.base).lowBound,new RangeEx(this.base).highBound);
		}
		else {
			BigInteger lowBound = ((YangInteger)(this.getDerived().getType())).getActualRange().getLowBound();
			BigInteger highBound = ((YangInteger)(this.getDerived().getType())).getActualRange().getHighBound();
			range = new Range(lowBound,highBound);
		}
		range.env = newSubEnv();
		return range;
	}
	
	public boolean checkValue(String value){
		if (null == value){
			return false;
		}
		
		//��鷶Χ
		if (null != this.getActualRange()){
			//Integer integer = new Integer(value);
			
			if (!this.getActualRange().isInRange(new BigInteger(value))){
				return false;
			}
		}
		return true;
		
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#validate()
	 */
	@Override
	public Validator validate() {
		Validator validator = new Validator();
		
		//�����ڳ��ȵĻ�����鳤�ȵ������Ƿ���ȷ		
		if (null != this.range){
			Validator rValidator = range.validate();
			validator.copy(rValidator);
			if (!isValidRange(this.range)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this, ErrTag.ERROR,"invalid range."));
			}
			
		}
		Validator sValidator = super.validate();
		validator.copy(sValidator);
		return validator;
		
		
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toYang()
	 */
	@Override
	public YangStatement toYang() {

		
		YangStatement integer = new YangStatement(YangStatement.YANG_STATEMENT_TYPE,this.name);
		if (null != this.range){
			YangStatement rangestat =null;	
			Range range = this.range;
			rangestat = range.writeYang();
			integer.addChild(rangestat);
		}

		return integer;
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toYang()
	 */
	@Override
	public Element toYin() {

		
		Element integer = new Element(YangStatement.YANG_STATEMENT_TYPE,env.getNs());
		Attribute name = new Attribute(XMLTag.XML_TAG_NAME,this.name);
		integer.setAttribute(name);
		if (null != this.range){
			Element rangestat =null;	
			Range range = this.range;
			rangestat = range.writeYin();
			integer.addContent(rangestat);
		}

		return integer;
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toXML()
	 */
	@Override
	public Element toXML() {

		Element integer = new Element(XMLTag.XML_TAG_TYPE);
		Element type = null;
		if (true == this.isDerivedType){
			type = new Element(XMLTag.XML_TAG_DERIVEDTYPE);
		}
		else {
			type = new Element(XMLTag.XML_TAG_BASETYPE);
		}
		
		type.setText(this.name);
		integer.addContent(type);
		if (null != this.range){
			Element range = this.range.writeXML();
			integer.addContent(range);
		}
		
		return integer;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#isFieldMandatory(java.lang.String)
	 */
	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return super.isFieldMandatory(field);
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#readXML(org.jdom2.Element)
	 */
	@Override
	public int readXML(Element element) throws ModelException {
		if (null == element){
			return MODELELEMENT_OK;
		}
		if (element.getName().equals(XMLTag.XML_TAG_RANGE)){
			Env subEnv = newSubEnv();
			Range range = (Range)(ModelElement.parseXML(subEnv, element));
			if (false == this.setRange(range)){
				return MODELELEMENT_FAIL;
			}
		}
		else if (element.getName().equals(XMLTag.XML_TAG_NAME)){
			return MODELELEMENT_OK;
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#readYANG(com.netscenery.modeldesigner.util.YangStatement)
	 */
	@Override
	public int readYANG(YangStatement statement) throws YangParserException, ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}
		if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_RANGE)){
			Env subEnv = newSubEnv();
			Range range = (Range)(ModelElement.parseYANG(subEnv, statement));
			if (false == this.setRange(range)){
				return MODELELEMENT_FAIL;
			}
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#readYANG(com.netscenery.modeldesigner.util.YangStatement)
	 */
	@Override
	public int readYin(Element statement) throws ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}
		if (statement.getName().equals(YangStatement.YANG_STATEMENT_RANGE)){
			Env subEnv = newSubEnv();
			Range range = (Range)(ModelElement.parseYin(subEnv, statement));
			if (false == this.setRange(range)){
				return MODELELEMENT_FAIL;
			}
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}	

}
